"""
@file:      analog.py
@author:    tgl
@date       2019-10-26
"""


class Analog(object):
    """
    模拟量类，一个模拟量包含基本的:名称，值，系数，量程
    最后的实际值 = (量程下限 <= 值 * 系数 <= 量程上限)
    """

    def __init__(self, name: str = "", value: float = 0, coefficient: float = 1.0,
                 minimal: float = 0, maximal: float = 100):
        """
        实例化一个模拟量数据对象
        :param name: 数据名称，字符串型
        :param value: 模拟量的值，浮点数型
        :param coefficient: 模拟量对应的的系统值，浮点数型
        :param minimal: 模拟量的最小值，最后的实际值(模拟量值*系数)应不小于该值
        :param maximal: 模所量的最大值，最后的实际值(模拟量值*系数)应不大于该值
        """
        self.name = name
        self.__valueRange = (0, 100)  # 模拟量取值范围
        self.__coefficient = 1.0  # 模拟量系数
        self.__value = 0  # 当前值
        self.value_range = (minimal, maximal)
        self.coefficient = coefficient
        self.value = value

    @property
    def coefficient(self) -> float:
        """ 获取模拟量的系数值 """
        return self.__coefficient

    @coefficient.setter
    def coefficient(self, value: float):
        """ 设置模拟量的系数值，新设定的系统值必须是一个非0的数值型数 """
        # 系统必须是一个不为0的数字
        if type(value) is int or type(value) is float:
            if value != 0:
                self.__coefficient = value
            else:
                raise ZeroDivisionError("The coefficient must not zero")
        else:
            raise TypeError("type of value request int or float, but gived {0}".format(type(value)))

    @property
    def value_range(self) -> tuple:
        """ 获取模拟量值范围 -> (最小值, 最大值) """
        return self.__valueRange

    @value_range.setter
    def value_range(self, value_range: tuple):
        """ 设置模拟量值的范围 """
        try:
            low, high = value_range

            if float(low) > float(high):
                raise ValueError("range[0]:{0} must less than range[1]:{1}".format(low, high))
            else:
                self.__valueRange = (float(low), float(high))
        except Exception as e:
            raise e

    @property
    def value(self) -> float:
        """ 获取模拟量值 """
        return self.__value

    @value.setter
    def value(self, value: float):
        """ 设备模拟量值 """
        self.__value = float(value)

    @property
    def actual_value(self) -> float:
        """ 获取模拟量最终的值，最终值=值*系数"""
        low, high = self.value_range
        value = self.value * self.coefficient
        if value < low:
            return low
        if value > high:
            return high
        return value

    def __iter__(self):
        """ 使Analog是一个可迭代对象 """
        self.__nextCounter = 0
        return self

    def __next__(self):
        """
        使Analog成为一个迭代器，每次都返回一个(名称，值)的元祖
        for循环,dict,list,tuple类型转换都会用到它
        """
        namelist = "name value actual_value value_range coefficient ".split()
        if self.__nextCounter < len(namelist):
            out = (namelist[self.__nextCounter], self.__getattribute__(namelist[self.__nextCounter]))
            self.__nextCounter += 1
            return out
        else:
            self.__nextCounter = 0
            raise StopIteration

    def __repr__(self):
        """ 对象转换在原字符 repr 函数或{0!s}格式化字符串时会调用它"""
        return "{0} {1}".format(self.__class__, dict(self))

    def __str__(self):
        """ 对象转换在字符串，print format str函数都会调用它"""
        return "{0}".format(dict(self))


class AnalogWithOffset(Analog):
    """
    带基础偏移的模拟量类
    如：一个电流模拟量为检测是否断线，用4mA电流来表示实际的0A电流
    实际值 = (值-偏移量) * 系数
    """

    def __init__(self, name: str = '', value: float = 0, offset: float = 0, coefficient: float = 1,
                 minimal: float = 0, maximal: float = 100):
        self.__offset = offset
        super().__init__(name, value, coefficient, minimal, maximal)

    @property
    def offset(self):
        return self.__offset

    @offset.setter
    def offset(self, value: float):
        self.__offset = value

    # @property
    # def value(self):
    #     return self.__value
    #
    # @value.setter
    # def value(self, value: float):
    #     self.__value = value + self.offset

    @property
    def actual_value(self):
        return (self.value + self.offset) * self.coefficient

    def __iter__(self):
        self.__nextCounter = 0
        return self

    def __next__(self):
        namelist = "name value actual_value offset coefficient value_range".split()
        if self.__nextCounter < len(namelist):
            out = (namelist[self.__nextCounter], self.__getattribute__(namelist[self.__nextCounter]))
            self.__nextCounter += 1
            return out
        else:
            self.__nextCounter = 0
            raise StopIteration


if __name__ == '__main__':
    A = Analog('test', 50)
    print('{0}'.format(A))
    print(A)  # {'name': 'test', 'value': 50, 'actual_value': 50.0, 'value_range': (0, 100), 'coefficient': 1.0}
    print('{0!r}'.format(A))  # <==> print(repr(A))
    print(str(A))
    print(dict(A))
    print(list(dict(A).keys()))
    print(list(dict(A).values()))
    print(list(A))
    print(tuple(A))


    B = AnalogWithOffset()
    B.offset = -4
    B.value = 5
    print(B)  # {'name': '', 'value': 5, 'actual_value': 1, 'offset': -4, 'coefficient': 1, 'value_range': (0, 100)}
    B.coefficient = 2
    print(B)  # {'name': '', 'value': 5, 'actual_value': 2, 'offset': -4, 'coefficient': 2, 'value_range': (0, 100)}
